﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;

namespace Arduino.ConnectionTypes.SerialPort
{
    public class Serial: ConnectionBase
    {
        private System.IO.Ports.SerialPort mPort = null;
        private System.Threading.Thread mMainThread = null;
        private bool mStoptThread = false;

        public Serial()
        {
            Error = "";
            mMainThread = System.Threading.Thread.CurrentThread;

            System.Threading.Thread thread_start = new System.Threading.Thread(ThreadListener);
            string Name_start = "SerialCommunication";
            thread_start.Name = Name_start;
            thread_start.Start();
        }

        ~Serial()
        {
            Close();
        }

        #region Data
        private void ThreadListener()
        {
            while (!mStoptThread)
            {
                try
                {
                    if (this.mMainThread != null && (this.mMainThread.ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped) return;
                }
                catch (Exception)
                {
                    //Inc.Logs.Log(ex);
                    return;
                }
                System.Threading.Thread.Sleep(1500);

                if (mPort != null && mPort.IsOpen && mPort.BytesToRead > 0) serialPort_DataReceived(this, null);
            }

        }

        private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (mPort == null) return;
            if (mMainThread != null && mMainThread.ThreadState == System.Threading.ThreadState.Stopped) mPort.Close();

            try
            {
                while (mPort.BytesToRead > 0)
                {
                    int ToRead = mPort.BytesToRead;
                    byte[] data = new byte[ToRead];
                    int i = 0;
                    while (i < ToRead)
                    {
                        mPort.Read(data, i, 1);
                        if (data[i] == 10)
                        {
                            data[i] = 0;
                            if (data[i - 1] == 13)
                            {
                                data[i - 1] = 0;
                                i--;
                            }

                            break;
                        }
                        i++;
                    }

                    if (i > 0) this.CallEventOnData(data, i);
                }
            }
            catch (Exception)
            {
                //Inc.Logs.Log(ex);
            }
        }

        private void serialPort_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
        {
            if (mPort == null) return;
            if (mMainThread != null && mMainThread.ThreadState == System.Threading.ThreadState.Stopped) mPort.Close();

            this.CallEventOnError(e.EventType.ToString());
        }
        #endregion

        #region Property
        public System.IO.Ports.SerialPort Port
        {
            get { return mPort; }
        }

        public string Error
        {
            get;
            private set;
        }
        #endregion

        #region Other
        public bool Open(int port, int rate)
        {
            if (port <= 0)
            {
                //throw new Exception("Port number must be more than 0.");
                Error = "Port number must be more than 0.";
                return false;
            }

            if (rate <= 0)
            {
                //throw new Exception("Port baud rate must be more than 0.");
                Error = "Port baud rate must be more than 0.";
                return false;
            }

            Close();

            Console.WriteLine("Port num = {0}", port);

            try
            {
                System.IO.Ports.SerialPort _port = new System.IO.Ports.SerialPort(String.Format("COM{0}", port), rate);
                _port.ErrorReceived += new SerialErrorReceivedEventHandler(serialPort_ErrorReceived);

                _port.DataBits = 8;
                _port.Parity = Parity.None;
                _port.StopBits = StopBits.One;
                _port.WriteTimeout = 10;

                _port.Open();
                mPort = _port;

                return true;
            }
            catch (Exception ex)
            {
                Error = String.Format("Error opening port - {0}", ex.Message);
                //throw new Exception(String.Format("Error opening port - {0}",ex.Message));
            }
            return false;
        }

        public void Close()
        {
            if (mPort != null)
            {
                if (mPort.IsOpen)
                {
                    Console.Write("Port opened. Closing port...");
                    try
                    {
                        mPort.Close();
                        Console.WriteLine("closed successfully.");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("cannot close - {0}",ex.Message);
                    }
                }
                mPort = null;
            }
        }
        #endregion

        #region Write
        // Write to the serial port.  This blocks until the data is
        // sent (or in a buffer to be sent).  All bytes are written,
        // unless there is some sort of error.
        public override int Write(int data)
        {
            byte[] b = BitConverter.GetBytes(data);
            int len = 1;
            //if (b[0] > 0) len = 1;
            if (b[1] > 0) len = 2;
            if (b[2] > 0) len = 3;
            if (b[3] > 0) len = 4;

            return Write(b, len);
        }

        public override int Write(int[] ptr, int len)
        {
            //Console.WriteLine("Write {0}", len);
            if (mPort == null || !mPort.IsOpen) return -1;

            byte[] _data = new byte[len * 4];

            for (int i = 0; i < len; i++)
            {
                byte[] b = BitConverter.GetBytes(ptr[i]);
                _data[i * 4] = b[0];
                _data[i * 4 + 1] = b[1];
                _data[i * 4 + 2] = b[2];
                _data[i * 4 + 3] = b[3];
            }

            mPort.Write(_data, 0, len*4);
            return 0;
        }

        public override int Write(byte[] ptr, int len)
        {
	        //Console.WriteLine("Write {0}", len);
	        if (mPort == null || !mPort.IsOpen) return -1;

            mPort.Write(ptr, 0, len);
	        return 0;

        }
        #endregion
    }
}
